﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Web;

namespace xunsearch.lib
{
    public class XSCommand 
    {
        public int arg 
        {
            get { return (arg1 << 8) | arg2; }
            set 
            { 
                arg1 = (value >> 8) & 0xff;
                arg2 = value & 0xff;
            }
        }
        /// <summary>
        /// 命令代码
        /// 通常是预定义常量 CMD_xxx, 取值范围 0~255
        /// </summary>
        public int cmd = xs_cmd.CMD_NONE;

        /// <summary>
        /// 参数1
        /// 取值范围 0~255, 具体含义根据不同的 CMD 而变化
        /// </summary>
        public int arg1 = 0;

        /// <summary>
        /// 参数2
        /// 取值范围 0~255, 常用于存储 value no, 具体参照不同 CMD 而确定
        /// </summary>
        public int arg2 = 0;

        /// <summary>
        /// 主数据内容, 最长 2GB
        /// </summary>
        public string buf = "";

        /// <summary>
        /// 辅数据内容, 最长 255字节
        /// </summary>
        public string buf1 = "";

        public XSCommand(Dictionary<string,string> cmd,int _arg1=0,int _arg2=0,string _buf="",string _buf1="")
        {
            foreach (var de in cmd) 
            {
                switch (de.Key) 
                {
                    case "arg1":
                        arg1 = int.Parse(de.Value);
                        break;
                    case "arg2":
                        arg2 = int.Parse(de.Value);
                        break;
                    case "buf":
                        buf = de.Value;
                        break;
                    case "buf1":
                        buf1 = de.Value;
                        break;
                }
            }
        }
        public XSCommand(int _cmd, int _arg1 = 0, int _arg2 = 0, string _buf = "", string _buf1 = "") 
        {
            cmd = _cmd;
            arg1 = _arg1;
            arg2 = _arg2;
            buf = _buf;
            buf1 = _buf1;
        }

        /// <summary>
        /// 转换为封包字符串
        /// </summary>
        /// <returns></returns>
        public string toString() 
        {
            if (buf1.Length > 0xff) 
            {
                buf1 = buf1.Substring(0, 0xff);
            }
            ///pack('CCCCI'
            int a = buf.Length;
            int a1=a;
            int a2=a>>7;
            int a3=a>>15;
            int a4=a>>23;
            return string.Format("{0}{1}{2}{3}{4}{5}{6}{7}", (char)cmd, (char)arg1, (char)arg2, (char)buf1.Length, (char)a1, (char)a2, (char)a3, (char)a4) + buf + buf1;
        }
        /// <summary>
        /// 获取属性 arg 的值
        /// </summary>
        /// <returns></returns>
        public int getArg() 
        {
            return arg2 | (arg1 << 8);
        }

        /// <summary>
        /// 设置属性 arg 的值
        /// </summary>
        /// <param name="arg"></param>
        public void setArg(int arg) 
        {
            arg1 = (arg >> 8) & 0xff;
            arg2 = arg & 0xff;
        }

    }
    public class XSServer
    {
        const int FILE = 0x01;
        const int BROKEN = 0x02;
        protected string _sendBuffer;

        protected static Socket _sock;
        protected string _conn;
        protected int _flag;
        protected string _project;

        public XS xs;

        /// <summary>
        /// 构造函数, 打开连接
        /// </summary>
        /// <param name="conn"></param>
        /// <param name="_xs"></param>
        public XSServer(string conn = "", XS _xs = null) 
        {
            xs = _xs;
            if (conn != "") 
            {
                open(conn);
            }
        }
        /// <summary>
        /// 析构函数
        /// </summary>
        public void __destruct() 
        {
            xs = null;
            close();
        }
        /// <summary>
        /// 打开服务端连接
        /// </summary>
        /// <param name="conn"></param>
        public void open(string conn) 
        {
            close();
            _conn = conn;
            _flag = BROKEN;
            _sendBuffer = "";
            _project = "";
            connent();
            _flag ^= BROKEN;
            if (xs != null) 
            {
                string name = xs.getName();
                setProject(name);
            }
        }
        /// <summary>
        /// 重新打开连接
        /// </summary>
        /// <param name="force"></param>
        /// <returns></returns>
        public XSServer reopen(bool force = false) 
        {
            if ((_flag & BROKEN) > 0 || force == true) 
            {
                open(_conn);
            }
            return this;
        }

        /// <summary>
        /// 关闭连接
        /// 附带发送发送 quit 命令
        /// </summary>
        /// <param name="ioerr"></param>
        public void close(bool ioerr = false) 
        {
            if (_sock != null && (_flag & BROKEN) != 0) 
            {
                if (ioerr == false && !string.IsNullOrWhiteSpace(_sendBuffer) ) 
                {
                    write(_sendBuffer);
                    _sendBuffer = "";
                }
                if(ioerr==false && (_flag& FILE)==0)
                {
                    XSCommand cmd = new XSCommand(xs_cmd.CMD_QUIT);                   
                    string msg = cmd.toString();
                    _sock.Send(Tool.ToByteArray(msg), msg.Length, 0);
                }
                _sock.Close();
                _flag |= BROKEN;
            }
            
        }

        /// <summary>
        /// 连接字符串
        /// </summary>
        /// <returns></returns>
        public string getConnstring() 
        {
            string str = _conn;
            if (Tool.isNumber(str)) 
            {
                str = "localhost:" + str;
            }
            else if (str.IndexOf(":") <= 0) 
            {
                str = "unix://" + str;
            }
            return str;
        }

        /// <summary>
        /// 获取连接资源描述符
        /// </summary>
        /// <returns>接标识, 仅用于内部测试等目的</returns>
        public Socket getSocket() 
        {
            return _sock;
        }
        /// <summary>
        /// 获取当前项目名称
        /// </summary>
        /// <returns></returns>
        public string getProject() 
        {
            return _project;
        }

        /// <summary>
        /// 设置当前项目
        /// </summary>
        /// <param name="name">项目名称</param>
        /// <param name="home"> 项目在服务器上的目录路径, 可选参数(不得超过255字节)</param>
        public void setProject(string name, string home = "") 
        {
            if (name != _project) 
            {
                XSCommand cmd = new XSCommand(xs_cmd.CMD_USE, 0, 0, name, home);
                execCommand(cmd, xs_cmd.CMD_OK_PROJECT);
                _project = name;
            }
        }
        /// <summary>
        /// 设置服务端超时秒数
        /// </summary>
        /// <param name="sec">int $sec 秒数, 设为 0则永不超时直到客户端主动关闭</param>
        public void setTimeout(int sec) 
        {
            XSCommand cmd = new XSCommand(xs_cmd.CMD_TIMEOUT);
            cmd.arg = sec;
            execCommand(cmd, xs_cmd.CMD_OK_TIMEOUT_SET);
        }

        
        public XSCommand execCommand(int _cmd ,int res_arg=xs_cmd.CMD_NONE,int res_cmd=xs_cmd.CMD_OK ) 
        {

            XSCommand cmd = new XSCommand(_cmd);
            return execCommand(cmd, res_arg, res_cmd);
        }
        
        /// <summary>
        /// 执行服务端指令并获取返回值
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="res_arg"></param>
        /// <param name="res_cmd"></param>
        /// <returns></returns>
        public XSCommand execCommand(XSCommand cmd, int res_arg = xs_cmd.CMD_NONE, int res_cmd = xs_cmd.CMD_OK) 
        {
            if ((cmd.cmd & 0x80)>0)
            {
                _sendBuffer += cmd.toString();
                return null;
            }
            string buf = _sendBuffer + cmd.toString();            
            
            _sendBuffer = "";
            write(buf);
            if ((_flag & FILE) > 0) 
            {
                return null;
            }

            XSCommand res = getRespond();
            //check respond
            if (res.cmd == xs_cmd.CMD_ERR && res_cmd != xs_cmd.CMD_ERR) 
            {
                throw new Exception(res.buf + res.arg1.ToString());
            }
            if (res.cmd != res_cmd || (res_arg != xs_cmd.CMD_NONE && res.arg != res_arg))
            {
               
                throw new Exception("Unexpected respond {CMD:" + res.cmd.ToString() + ", ARG:" + res.arg.ToString() + "}");
            }

            return res;
        }

        /// <summary>
        /// 往服务器直接发送指令(无缓存)
        /// </summary>
        /// <param name="cmd"></param>
        public void sendCommand(int cmd) 
        {
            write(new XSCommand(cmd).toString());
        }
        /// <summary>
        /// 往服务器直接发送指令(无缓存)
        /// </summary>
        /// <param name="cmd"></param>
        public void sendCommand(XSCommand cmd) 
        {
            write(cmd.toString());
        }

        /// <summary>
        /// 从服务器读取响应指令
        /// </summary>
        /// <returns>成功返回响应指令</returns>
        public XSCommand getRespond()
        {
            string buf = read(8);
          
            ///unpack('Ccmd/Carg1/Carg2/Cblen1/Iblen', $buf)
            byte[] buf1 = Tool.ToByteArray(buf);
            int cmd = buf1[0];
            int arg1 = buf1[1];
            int arg2 = buf1[2];
            int blen1 = buf1[3];
            int blen = (buf1[7] << 23) + (buf1[6] << 15) + (buf1[5] << 7) + buf1[4];            
            
            XSCommand res = new XSCommand(cmd, arg1, arg2);
            res.buf = read(blen);
            res.buf1 = read(blen1);           
            return res;

        }

        /// <summary>
        /// 判断服务端是否有可读数据
        /// </summary>
        /// <returns>如果有返回 true, 否则返回 false</returns>
        public bool hasRespond() 
        {
            if (_sock == null  || (_flag & (BROKEN | FILE)) > 0) 
            {
                return false;
            }
            return true;
        }
        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="buf">要写入的字符串</param>
        /// <param name="len">要写入的长度, 默认为字符串长度</param>
        protected void write(string buf, int len = 0) 
        {
            int size = 0;
            len = size = buf.Length;
            if (buf.Length == 0) 
            {
                return;
            }
            check();
            //int bytes = 0;
            while (true) 
            {
                byte[] b = Tool.ToByteArray(buf);
                
                _sock.Send(b, b.Length, 0);//发送信息
                break;
            }
        }
        /// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="len">要读入的长度</param>
        /// <returns></returns>
        protected string read(int len) 
        {
          
            if (len == 0) 
            {
                return "";
            }
            check();
            string buf="";
            int size=0;
            while ((size = len) > 0)
            {
                try
                {
                    byte[] data = new byte[len];
                    int bytes = _sock.Receive(data, data.Length, 0);
                    if (bytes == 0)
                    {
                        break;
                    }
                    len -= data.Length;
                    buf += Tool.ByteArrayToString(data);
                    if (len == 0)
                    {                        
                        return buf;
                    }
                }
                catch (Exception ex) 
                {
                    HttpContext.Current.Response.Write(ex.Message);
                    HttpContext.Current.Response.End();
                }
               
            }
            throw new Exception(string.Format("Failed to recv the data from server completely (SIZE:{0}/{1},REASON)", size - len, size));
        }  

        /// <summary>
        /// 检测服务端的连接情况
        /// </summary>
        protected void check()
        {
            if (_sock == null)
            {
                throw new Exception("No server connection");
            }
            if ((_flag & BROKEN) > 0)
            {
                throw new Exception("Broken server connection");
            }
        }

        /// <summary>
        /// 连接服务端
        /// </summary>
        protected void connent() 
        {
            string conn = _conn;
            string host = "";
            int port = 0;
            if (Tool.isNumber(conn)) 
            {
                host = "127.0.0.1";
                port = int.Parse(conn);
            }
            else if (conn.Replace(":", "") != conn) 
            {
                host = conn.Substring(0, conn.IndexOf(":"));
                port = int.Parse(conn.Substring(conn.IndexOf(":") + 1, conn.Length - conn.IndexOf(":") - 1));
            }
            try
            {
                IPAddress ip = IPAddress.Parse(host);//把ip地址字符串转换为IPAddress类型的实例
                IPEndPoint ipe = new IPEndPoint(ip, port);//用指定的端口和ip初始化IPEndPoint类的新实例
                _sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);////创建一个socket对像，如果用udp协议，则要用SocketType.Dgram类型的套接字
                _sock.Connect(ipe);
            }
            catch(Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
    }
}
